 /*******************************************************************************
  * Copyright (c) 2003, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.osgi.framework.internal.core;

 import java.io.*;
 import java.net.URL ;
 import java.security.*;
 import java.util.ArrayList ;
 import org.eclipse.osgi.framework.adaptor.BundleProtectionDomain;
 import org.eclipse.osgi.framework.adaptor.PermissionStorage;
 import org.eclipse.osgi.framework.debug.Debug;
 import org.osgi.framework.AdminPermission;
 import org.osgi.framework.FrameworkEvent;
 import org.osgi.service.permissionadmin.PermissionAdmin;
 import org.osgi.service.permissionadmin.PermissionInfo;

 /**
  * Permission Admin service for the OSGi specification.
  *
  * The Permission Admin service allows operators to
  * manage the permissions of bundles. There is at most one Permission Admin
  * service present in the Framework.
  * <p>
  * Access to the Permission Admin service is protected by
  * corresponding
  * <tt>ServicePermission</tt>. In addition the <tt>AdminPermission</tt>
  * is required to actually set permissions.
  *
  * <p>Bundle permissions are managed using a permission table. A bundle's location
  * serves as the key into this permission table. The value of a table entry is
  * the set of permissions (of type <tt>PermissionInfo</tt>) granted to the
  * bundle with the given location.
  * A bundle may have an entry in the permission table prior to being installed
  * in the Framework.
  *
  * <p>The permissions specified in <tt>setDefaultPermissions</tt> are used as the
  * default
  * permissions which are granted to all bundles that do not have an entry in
  * the permission table.
  *
  * <p>Any changes to a bundle's permissions in the permission table will take
  * effect no later than when bundle's <tt>java.security.ProtectionDomain</tt>
  * is involved in a permission check, and will be made persistent.
  *
  * <p>Only permission classes on the system classpath or from an exported
  * package are considered during a permission check.
  * Additionally, only permission classes that are subclasses of
  * <tt>java.security.Permission</tt> and define a 2-argument constructor
  * that takes a <i>name</i> string and an <i>actions</i> string can be used.
  * <p>
  * Permissions implicitly granted by the Framework (for example, a bundle's
  * permission to access its persistent storage area) cannot be changed, and
  * are not reflected in the permissions returned by <tt>getPermissions</tt>
  * and <tt>getDefaultPermissions</tt>.
  */
 public class PermissionAdminImpl implements PermissionAdmin {
     private static final String ADMIN_IMPLIED_ACTIONS = AdminPermission.RESOURCE + ',' + AdminPermission.METADATA + ',' + AdminPermission.CLASS;

     /** framework object */
     protected Framework framework;

     /** permission storage object */
     protected PermissionStorage storage;

     /** The permissions to use if no other permissions can be determined */
     protected PermissionInfo[] defaultDefaultPermissionInfos;

     /** The basic implied permissions for a bundle */
     protected PermissionInfo[] baseImpliedPermissionInfos;

     /** The permission collection containing the default assigned permissions */
     protected BundleCombinedPermissions defaultAssignedPermissions;

     /**
      * Construstor.
      *
      * @param framework Framework object.
      */
     protected PermissionAdminImpl(Framework framework, PermissionStorage storage) {
         this.framework = framework;
         this.storage = storage;

         defaultDefaultPermissionInfos = getPermissionInfos(getClass().getResource(Constants.OSGI_DEFAULT_DEFAULT_PERMISSIONS));
         baseImpliedPermissionInfos = getPermissionInfos(getClass().getResource(Constants.OSGI_BASE_IMPLIED_PERMISSIONS));

         if (Debug.DEBUG && Debug.DEBUG_SECURITY) {
             Debug.println("Default default assigned bundle permissions"); //$NON-NLS-1$
 if (defaultDefaultPermissionInfos == null) {
                 Debug.println(" <none>"); //$NON-NLS-1$
 } else {
                 for (int i = 0; i < defaultDefaultPermissionInfos.length; i++) {
                     Debug.println(" " + defaultDefaultPermissionInfos[i]); //$NON-NLS-1$
 }
             }

             Debug.println("Base implied bundle permissions"); //$NON-NLS-1$
 if (baseImpliedPermissionInfos == null) {
                 Debug.println(" <none>"); //$NON-NLS-1$
 } else {
                 for (int i = 0; i < baseImpliedPermissionInfos.length; i++) {
                     Debug.println(" " + baseImpliedPermissionInfos[i]); //$NON-NLS-1$
 }
             }
         }

         defaultAssignedPermissions = new BundleCombinedPermissions(null);
         defaultAssignedPermissions.setAssignedPermissions(createDefaultAssignedPermissions(getDefaultPermissions()), true);
     }

     /**
      * Gets the permissions assigned to the bundle with the specified
      * location.
      *
      * @param location The location of the bundle whose permissions are to
      * be returned.
      *
      * @return The permissions assigned to the bundle with the specified
      * location, or <tt>null</tt> if that bundle has not been assigned any
      * permissions.
      */
     public PermissionInfo[] getPermissions(String location) {
         if (location == null) {
             throw new NullPointerException ();
         }

         PermissionStorage storage = new org.eclipse.osgi.framework.internal.core.SecurePermissionStorage(this.storage);

         try {
             String [] data = storage.getPermissionData(location);

             if (Debug.DEBUG && Debug.DEBUG_SECURITY) {
                 Debug.println("Getting permissions for location: " + location); //$NON-NLS-1$
 if (data == null) {
                     Debug.println(" <none>"); //$NON-NLS-1$
 } else {
                     for (int i = 0; i < data.length; i++) {
                         Debug.println(" " + data[i]); //$NON-NLS-1$
 }
                 }
             }

             return makePermissionInfo(data);
         } catch (IOException e) {
             framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, e);

             return null;
         }
     }

     /**
      * Assigns the specified permissions to the bundle with the specified
      * location.
      *
      * @param location The location of the bundle that will be assigned the
      * permissions.
      * @param permissions The permissions to be assigned, or <tt>null</tt>
      * if the specified location is to be removed from the permission table.
      * @exception SecurityException if the caller does not have the
      * <tt>AllPermission</tt>.
      */
     public void setPermissions(String location, PermissionInfo[] permissions) {
         SecurityManager sm = System.getSecurityManager();
         if (sm != null)
             sm.checkPermission(new AllPermission());
         if (location == null) {
             throw new NullPointerException ();
         }

         PermissionStorage storage = new org.eclipse.osgi.framework.internal.core.SecurePermissionStorage(this.storage);

         try {
             String [] data = makePermissionData(permissions);

             if (Debug.DEBUG && Debug.DEBUG_SECURITY) {
                 Debug.println("Setting permissions for location: " + location); //$NON-NLS-1$
 if (data == null) {
                     Debug.println(" <none>"); //$NON-NLS-1$
 } else {
                     for (int i = 0; i < data.length; i++) {
                         Debug.println(" " + data[i]); //$NON-NLS-1$
 }
                 }
             }

             storage.setPermissionData(location, data);
         } catch (IOException e) {
             framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, e);

             return;
         }

         AbstractBundle bundle = framework.getBundleByLocation(location);

         if ((bundle != null) && (bundle.getBundleId() != 0)) {
             ProtectionDomain domain = bundle.getProtectionDomain();

             if (domain != null) {
                 BundleCombinedPermissions combined = (BundleCombinedPermissions) domain.getPermissions();

                 if (permissions == null) {
                     combined.setAssignedPermissions(defaultAssignedPermissions, true);
                 } else {
                     combined.setAssignedPermissions(createPermissions(permissions, bundle, false), false);
                 }
             }
         }
     }

     /**
      * Returns the bundle locations that have permissions assigned to them,
      * that is, bundle locations for which an entry
      * exists in the permission table.
      *
      * @return The locations of bundles that have been assigned any
      * permissions, or <tt>null</tt> if the permission table is empty.
      */
     public String [] getLocations() {
         PermissionStorage storage = new org.eclipse.osgi.framework.internal.core.SecurePermissionStorage(this.storage);

         try {
             String [] locations = storage.getLocations();

             return locations;
         } catch (IOException e) {
             framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, e);

             return null;
         }
     }

     /**
      * Gets the default permissions.
      *
      * <p>These are the permissions granted to any bundle that does not
      * have permissions assigned to its location.
      *
      * @return The default permissions, or <tt>null</tt> if default
      * permissions have not been defined.
      */
     public PermissionInfo[] getDefaultPermissions() {
         PermissionStorage storage = new org.eclipse.osgi.framework.internal.core.SecurePermissionStorage(this.storage);

         try {
             String [] data = storage.getPermissionData(null);

             if (Debug.DEBUG && Debug.DEBUG_SECURITY) {
                 Debug.println("Getting default permissions"); //$NON-NLS-1$
 if (data == null) {
                     Debug.println(" <none>"); //$NON-NLS-1$
 } else {
                     for (int i = 0; i < data.length; i++) {
                         Debug.println(" " + data[i]); //$NON-NLS-1$
 }
                 }
             }

             return makePermissionInfo(data);
         } catch (IOException e) {
             framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, e);

             return null;
         }
     }

     /**
      * Sets the default permissions.
      *
      * <p>These are the permissions granted to any bundle that does not
      * have permissions assigned to its location.
      *
      * @param permissions The default permissions.
      * @exception SecurityException if the caller does not have the
      * <tt>AllPermission</tt>.
      */
     public void setDefaultPermissions(PermissionInfo[] permissions) {
         SecurityManager sm = System.getSecurityManager();
         if (sm != null)
             sm.checkPermission(new AllPermission());
         PermissionStorage storage = new org.eclipse.osgi.framework.internal.core.SecurePermissionStorage(this.storage);

         try {
             String [] data = makePermissionData(permissions);

             if (Debug.DEBUG && Debug.DEBUG_SECURITY) {
                 Debug.println("Setting default permissions"); //$NON-NLS-1$
 if (data == null) {
                     Debug.println(" <none>"); //$NON-NLS-1$
 } else {
                     for (int i = 0; i < data.length; i++) {
                         Debug.println(" " + data[i]); //$NON-NLS-1$
 }
                 }
             }

             storage.setPermissionData(null, data);
         } catch (IOException e) {
             framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, e);

             return;
         }

         defaultAssignedPermissions.setAssignedPermissions(createDefaultAssignedPermissions(permissions), true);
     }

     /**
      * Make a PermissionInfo array from an array of encoded permission Strings.
      *
      * @param data Array of encoded permission Strings
      * @return Array of PermissionInfo objects.
      */
     protected PermissionInfo[] makePermissionInfo(String [] data) {
         if (data == null) {
             return null;
         }

         int size = data.length;

         PermissionInfo[] permissions = new PermissionInfo[size];

         for (int i = 0; i < size; i++) {
             permissions[i] = new PermissionInfo(data[i]);
         }

         return permissions;
     }

     /**
      * Make an array of encoded permission Strings from a PermissionInfo array.
      *
      * @param permissions Array of PermissionInfor objects.
      * @return Array of encoded permission Strings
      */
     protected String [] makePermissionData(PermissionInfo[] permissions) {
         if (permissions == null) {
             return null;
         }

         int size = permissions.length;

         String [] data = new String [size];

         for (int i = 0; i < size; i++) {
             data[i] = permissions[i].getEncoded();
         }

         return data;
     }

     /**
      * This method is called by the Bundle object to create the
      * PermissionCollection used by the bundle's ProtectionDomain.
      *
      * @param bundle the bundle object
      * @return BundleCombinedPermission object with the bundle's
      * dynamic permissions.
      */
     protected BundleProtectionDomain createProtectionDomain(AbstractBundle bundle) {
         BundlePermissionCollection implied = getImpliedPermissions(bundle);

         BundleCombinedPermissions combined = new BundleCombinedPermissions(implied);

         BundlePermissionCollection assigned = getAssignedPermissions(bundle);

         combined.setAssignedPermissions(assigned, assigned == defaultAssignedPermissions);

         combined.setConditionalPermissions(new ConditionalPermissions(bundle, framework.condPermAdmin));

         /* now process the permissions.perm file, if it exists, and build the
          * restrictedPermissions using it. */
         PermissionInfo[] permInfos = getPermissionInfos(bundle.getEntry("OSGI-INF/permissions.perm")); //$NON-NLS-1$
 if (permInfos != null) {
             ConditionalPermissionInfoImpl cpiArray[] = new ConditionalPermissionInfoImpl[1];
             cpiArray[0] = new ConditionalPermissionInfoImpl(null, ConditionalPermissionAdminImpl.EMPTY_COND_INFO, permInfos);
             ConditionalPermissionSet cps = new ConditionalPermissionSet(bundle, cpiArray, ConditionalPermissionAdminImpl.EMPTY_COND);
             combined.setRestrictedPermissions(cps);
         }

         return new BundleProtectionDomainImpl(bundle, combined);
     }

     /**
      * Creates the default assigned permissions for bundles that
      * have no assigned permissions.
      * The default permissions are assigned via the PermissionAdmin service
      * and may change dynamically.
      *
      * @return A PermissionCollection of the default assigned permissions.
      */
     protected BundlePermissionCollection createDefaultAssignedPermissions(PermissionInfo[] info) {
         if (Debug.DEBUG && Debug.DEBUG_SECURITY) {
             Debug.println("Creating default assigned permissions"); //$NON-NLS-1$
 }

         if (info == null) {
             info = defaultDefaultPermissionInfos;
         }

         return createPermissions(info, null, false);
     }

     /**
      * Returns the assigned permissions for a bundle.
      * These permissions are assigned via the PermissionAdmin service
      * and may change dynamically.
      *
      * @param bundle The bundle to create the permissions for.
      * @return A PermissionCollection of the assigned permissions.
      */
     protected BundlePermissionCollection getAssignedPermissions(AbstractBundle bundle) {
         String location = bundle.getLocation();

         PermissionInfo[] info = getPermissions(location);

         if (info == null) {
             return defaultAssignedPermissions;
         }

         if (Debug.DEBUG && Debug.DEBUG_SECURITY) {
             Debug.println("Creating assigned permissions for " + bundle); //$NON-NLS-1$
 }

         return createPermissions(info, bundle, false);
     }

     /**
      * Returns the implied permissions for a bundle.
      * These permissions never change.
      *
      * @param bundle The bundle to create the permissions for.
      * @return A PermissionCollection of the implied permissions.
      */
     protected BundlePermissionCollection getImpliedPermissions(AbstractBundle bundle) {
         if (Debug.DEBUG && Debug.DEBUG_SECURITY)
             Debug.println("Creating implied permissions for " + bundle); //$NON-NLS-1$

         return createPermissions(baseImpliedPermissionInfos, bundle, true);
     }

     /**
      * Read the permissions from the specified resource.
      *
      * @return An array of PermissionInfo objects from the specified
      * resource.
      */
     protected PermissionInfo[] getPermissionInfos(URL resource) {
         if (resource == null)
             return null;
         PermissionInfo[] info = ConditionalPermissionAdminImpl.EMPTY_PERM_INFO;
         DataInputStream in = null;
         try {
             in = new DataInputStream(resource.openStream());
             ArrayList permissions = new ArrayList ();
             BufferedReader reader;
             try {
                 reader = new BufferedReader(new InputStreamReader(in, "UTF8")); //$NON-NLS-1$
 } catch (UnsupportedEncodingException e) {
                 reader = new BufferedReader(new InputStreamReader(in));
             }

             while (true) {
                 String line = reader.readLine();
                 if (line == null) /* EOF */
                     break;
                 line = line.trim();
                 if ((line.length() == 0) || line.startsWith("#") || line.startsWith("//")) /* comments */ //$NON-NLS-1$ //$NON-NLS-2$
 continue;

                 try {
                     permissions.add(new PermissionInfo(line));
                 } catch (IllegalArgumentException iae) {
                     /* incorrectly encoded permission */
                     framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, iae);
                 }
             }
             int size = permissions.size();
             if (size > 0)
                 info = (PermissionInfo[]) permissions.toArray(new PermissionInfo[size]);
         } catch (IOException e) {
             // do nothing
 } finally {
             try {
                 if (in != null)
                     in.close();
             } catch (IOException ee) {
                 // do nothing
 }
         }
         return info;
     }

     /**
      * Create a PermissionCollection from a PermissionInfo array.
      *
      * @param info Array of PermissionInfo objects.
      * @param bundle The target bundle for the permissions.
      * @return A PermissionCollection containing Permission objects.
      */
     protected BundlePermissionCollection createPermissions(PermissionInfo[] info, final AbstractBundle bundle, boolean implied) {
         if (info == null)
             info = new PermissionInfo[0];
         if (implied) {
             // create the implied AdminPermission actions for this bundle
 PermissionInfo impliedInfo = new PermissionInfo(AdminPermission.class.getName(), "(id=" + bundle.getBundleId() + ")", ADMIN_IMPLIED_ACTIONS); //$NON-NLS-1$ //$NON-NLS-2$
 if (Debug.DEBUG && Debug.DEBUG_SECURITY)
                 Debug.println("Created permission: " + impliedInfo); //$NON-NLS-1$
 PermissionInfo[] impliedInfos = new PermissionInfo[info.length + 1];
             System.arraycopy(info, 0, impliedInfos, 0, info.length);
             impliedInfos[info.length] = impliedInfo;
             info = impliedInfos;
         }
         ConditionalPermissionInfoImpl cpiArray[] = new ConditionalPermissionInfoImpl[1];
         cpiArray[0] = new ConditionalPermissionInfoImpl(null, ConditionalPermissionAdminImpl.EMPTY_COND_INFO, info);
         return new ConditionalPermissionSet(bundle, cpiArray, ConditionalPermissionAdminImpl.EMPTY_COND);
     }

 }

